package com.qiao.dfw.manager;


import com.qiao.dfw.config.Const;
import com.qiao.dfw.define.*;
import com.qiao.dfw.entity.Grid;
import com.qiao.dfw.entity.MapData;
import com.qiao.dfw.entity.Player;
import com.qiao.dfw.entity.PlayerLocal;
import com.qiao.dfw.loggic.ATask;
import com.qiao.dfw.loggic.ECode;
import com.qiao.dfw.proto.Battle;
import com.qiao.dfw.proto.Hall;
import com.qiao.dfw.proto.Role;
import com.qiao.dfw.utils.RandomUtil;

import java.util.*;

/**
 * @Author qzj
 * @Description 一个 room管理器
 * @Version 1.0
 */
public class RoomManager {

    public int id;          // 房间唯一id
    public int threadId;    // 对局线程id
    public RoomStatus roomStatus;       // 房间当前状态
    public BattleStatus battleStatus;   // 对局状态

    /**
     * 对局的玩家,0为先手
     */
    private List<Player> Players = new ArrayList<>();

    /**
     * 观战玩家
     */
    private List<Player> LookPlayer = new ArrayList<>();
    /**
     * 当前操作的 玩家 标记,从0开始,每次+1; 也用于记录 本局 一共执行的轮数
     */
    private int Index;
    /**
     * 当前玩家
     */
    private Player nowPlayer;

    // 下次 执行 时间
    public long nextRun;

    // 地图实时数据
    private final MapData mapData = new MapData();
    // 按照 index 角色拥有的对局道具
    private final BattleItemManager itemManager = new BattleItemManager();
    // 地图配置表
    private MapTab mapTab;

    /**
     * 当一个玩家可以 购买道具时,每次购买放入 这个list; 结束后,将所有道具放入玩家身上;
     */
    private List<Integer> nowBuyList = new ArrayList<>();

    public RoomManager(int id) {
        this.id = id;
    }

    /**
     * 1:roll点 确定谁先出牌
     */
    public void init(List<Player> players, int mapId) {
        roomStatus = RoomStatus.Init;
        Index = 0;
        long nowTime = System.currentTimeMillis();
        for (Player player : players) {
            player.room = this;
            player.local = PlayerLocal.Battle;
            player.battleData.rid = id;
        }
        Players.clear();
        // 随机确定 出手顺序
        for (int i = 0, length = players.size(); i < length; i++) {
            // 测试时不随机,按顺序来
            int ran = 0; //RandomUtil.getRandom(players.size());
            Player remove = players.remove(ran);
            Players.add(remove);
        }

        // 初始化 地图数据 到玩家
        mapTab = DataConfigManager.MapTabContainer.get(mapId);
        mapData.init(Players, mapTab);

        // 初始化 所有玩家的 道具列表
        itemManager.init(Players.size());

        // 通知玩家 进入对局
        Battle.BattleBeginResponse.Builder builder = Battle.BattleBeginResponse.newBuilder();
        for (int i = 0; i < Players.size(); i++) {
            Role.RoleInfo build = Players.get(i).build();
            builder.addInfos(build);
        }

        builder.setMap(mapId);
        byte[] byteArray = builder.build().toByteArray();
        sendMsgToAll(ECode.BattleBegin, byteArray);

        roomStatus = RoomStatus.Battle;
        battleStatus = BattleStatus.AwaitRoll;
        // 2秒后 通知客户端 开始 roll; 2秒是为了等待 客户端 加载进入战斗场景;也可以用其他方式来 解决
        nextRun = nowTime + 2000;
    }

    /**
     * 获取对局数据;
     *
     * @return
     */
    public Role.MapData getBattleData(Player linkPlayer) {
        Role.MapData.Builder builder = Role.MapData.newBuilder();
        builder.setMap(mapTab.Id);
        for (Player player : Players) {
            builder.addRoles(player.build());
        }
        for (Grid grid : mapData.grids) {
            builder.addGrids(grid.build());
        }
        for (int i : mapData.local) {
            builder.addLocals(i);
        }

        for (int gold : mapData.golds) {
            builder.addGolds(gold);
        }
        // 如果是对局中的玩法,返回 好运剩余回合/道具列表
        int index = getIndex(linkPlayer);
        if (index >= 0) {
            builder.setGood(mapData.getGood(index));
            Map<Integer, Integer> items = itemManager.getItems(index);
            for (Map.Entry<Integer, Integer> entry : items.entrySet()) {
                builder.putItems(entry.getKey(), entry.getValue());
            }
        }
        return builder.build();
    }

    /**
     * 该玩家重连了,检查是否该他 行动,是否需要 重新通知他 行动;
     *
     * @param player
     */
    public void relink(Player player) {
        if (player.uid == nowPlayer.uid) {
            // 是该玩家 行动中,检查对方剩余操作时间,如果大于3秒则重新通知
            int awaitTime = (int) (nextRun - System.currentTimeMillis()) / 1000;

            if (awaitTime >= 3) {
                switch (battleStatus) {
                    case AwaitRollSend -> {
                        // 等待玩家roll
                        Battle.SendRollResponse.Builder builder = Battle.SendRollResponse.newBuilder();
                        Battle.SendRollResponse build =
                                builder.setUid(nowPlayer.uid).setGood(mapData.getGood(getNowIndex())).setTime(awaitTime).build();
                        ATask.sendMsgToOne(player.uid, ECode.SendRollRoll, build.toByteArray());
                        return;
                    }
                    case MoveIng -> {
                        // 等待玩家移动结束,不需要处理,等服务器自己 update执行
                    }
                    case AwaitSel -> {
                        // 等待玩家选择时,可能处于 空地(询问是否建筑)/自家建筑(询问是否升级);;;商店有单独的状态(询问是否购买)
                        Battle.MoveTargetEndResponse.Builder builder = Battle.MoveTargetEndResponse.newBuilder();
                        Battle.MoveTargetEndResponse build;

                        Grid grid = mapData.getPlayerGrid(getNowIndex());
                        // 0:空地,收费类建筑:3商场,4宾馆,5饭店
                        if (grid.type == 0) {
                            build = builder.setTime(awaitTime).setUid(nowPlayer.uid).build();
                            ATask.sendMsgToOne(player.uid, ECode.MoveTargetEnd, build.toByteArray());
                        } else {
                            // 该建筑的类型;
                            BuildType tab = DataConfigManager.BuildTypeContainer.get(grid.type);
                            // 自家建筑,判断是否满级了;
                            if (grid.lv < tab.BuildGold.length) {
                                // 没满级,询问玩家是否升级
                                build = builder.setTime(awaitTime).setUid(nowPlayer.uid).build();
                                ATask.sendMsgToOne(player.uid, ECode.MoveTargetEnd, build.toByteArray());
                            }
                        }
                    }
                    case AwaitShop -> {
                        // 弹出商店,商店操作时间 30s
                        nowBuyList.clear();
                        Battle.MoveTargetEndResponse.Builder builder = Battle.MoveTargetEndResponse.newBuilder();
                        Battle.MoveTargetEndResponse build = builder.setTime(awaitTime).setUid(nowPlayer.uid).build();
                        ATask.sendMsgToOne(player.uid, ECode.MoveTargetEnd, build.toByteArray());
                        return;
                    }
                }
            }

        }
    }


    /**
     * 观战
     *
     * @param player
     */
    public void lookBattle(Player player) {
        for (Player item : LookPlayer) {
            if (item.uid == player.uid) {
                return;
            }
        }
        LookPlayer.add(player);
    }

    public void Update() {
        if (roomStatus == RoomStatus.Battle) {
            Battle();
        } else if (roomStatus == RoomStatus.Over) {
            closeRoom();
        }
    }

    private void Battle() {
        switch (battleStatus) {
            case AwaitRoll -> {
                sendRoll();
            }
            case AwaitRollSend -> {
                // 如果玩家在10秒内roll了,就不会执行这里; 这里自动roll
                playerRoll(nowPlayer, 0);
            }
            case MoveIng -> {
                moveEnd(nowPlayer);
            }
            case AwaitSel -> {
                awaitSel(nowPlayer, 0);
            }
            case AwaitShop -> {
                battleShop(nowPlayer, 0);
            }

        }
    }


    /**
     * 通知所有人,该 player 玩家roll了
     */
    private void sendRoll() {
        nowPlayer = Players.get(getNowIndex());
        Battle.SendRollResponse.Builder builder = Battle.SendRollResponse.newBuilder();
        Battle.SendRollResponse build =
                builder.setUid(nowPlayer.uid).setGood(mapData.getGood(getNowIndex())).setTime(Const.AwaitTime).build();
        sendMsgToAll(ECode.SendRollRoll, build.toByteArray());

        battleStatus = BattleStatus.AwaitRollSend;
        nextRun = System.currentTimeMillis() + Const.AwaitTime * 1000;// 最多11秒内 必须roll

    }

    /**
     * 通知服务器自己的roll点方式
     */
    public void playerRoll(Player player, int id) {
        if (!check(player, BattleStatus.AwaitRollSend)) {
            return;
        }
        int roll = 0;
        if (id == 0) {
            roll = RandomUtil.getRoll();
        } else if (id > 0) {
            // 使用了道具
            ItemTab tab = DataConfigManager.ItemTabContainer.get(id);
            if (tab == null) {
                return;
            }
            itemManager.remove(getNowIndex(), tab.Id);

            if (tab.Type == 1) {// 定向roll
                roll = tab.Value[0];
            } else if (tab.Type == 2) {// 范围roll
                roll = RandomUtil.getRandom(tab.Value[0], tab.Value[1]);
            } else if (tab.Type == 3) {// 好运来,遇到事件时,运气更好;
                mapData.addGood(getNowIndex(), tab.Value[0]);
            } else if (tab.Type == 4) {// 移动到 下一个事件点
                roll = mapData.getNextEventMove(getNowIndex());
            } else if (tab.Type == 5) {// 移动到下一个商店
                roll = mapData.getNextShopMove(getNowIndex());
            }
            // 通知所有人,有人使用了道具
            Battle.UseBattleItemResponse.Builder builder = Battle.UseBattleItemResponse.newBuilder();
            Battle.UseBattleItemResponse build = builder.setUid(player.uid).setCid(tab.Id).build();
            sendMsgToAll(ECode.BattleUseItem, build.toByteArray());

            // 如果使用道具后,还允许移动
            if (tab.CanMove) {
                return;
            }
        } else {
            // 留作测试
            roll = -id;
            if (roll > mapData.grids.size()) {
                roll = mapData.grids.size();
            }
        }

        // 在服务器实体地图中,移动玩家
        mapData.playerMove(getNowIndex(), roll);
        // 进入等待玩家 移动状态
        battleStatus = BattleStatus.MoveIng;
        // 假设 每走一格 耗时0.4秒
        int time = (int) (roll * 0.4f) + 1;

        // 在 time事件内,玩家 通知服务器 自己移动完成;如果没通知,则会被 update执行
        nextRun = System.currentTimeMillis() + time * 1000L;
        battleStatus = BattleStatus.MoveIng;

        Battle.RoleRollResponse.Builder builder = Battle.RoleRollResponse.newBuilder();
        Battle.RoleRollResponse build = builder.setUid(player.uid).setMove(roll).build();
        sendMsgToAll(ECode.RoleRoll, build.toByteArray());
    }

    /**
     * 客户端通知服务器,自己移动完成了;如果客户端超时没通知,则有update执行
     */
    public void moveEnd(Player player) {
        if (!check(player, BattleStatus.MoveIng)) {
            return;
        }
        Battle.MoveTargetEndResponse.Builder builder = Battle.MoveTargetEndResponse.newBuilder();
        Battle.MoveTargetEndResponse build;
        // 移动完成,根据当前 所在地块,确定触发的事件,或者没有事件触发,直接下一个玩家 roll
        Grid grid = mapData.getPlayerGrid(getNowIndex());
        // 0:空地, 1:事件点,2:商店;  收费类建筑:3商场,4宾馆,5饭店
        if (grid.type == 0) {
            build = builder.setTime(Const.AwaitTime).setUid(nowPlayer.uid).build();
            battleStatus = BattleStatus.AwaitSel;
            nextRun = System.currentTimeMillis() + Const.AwaitTime * 1000;
            sendMsgToAll(ECode.MoveTargetEnd, build.toByteArray());
        } else if (grid.type == 1) {
            // 事件; 可以抽出,放在一个对象里,每次 开局缓存下来;这里直接 每次计算了

            // 检查是否有好运卡
            int eventId = mapData.goodLuck(getNowIndex());
            EventTab tab = DataConfigManager.EventTabContainer.get(eventId + 1);
            // 解析事件
            if (tab.Type == 0) {
                // 无事发生
            } else if (tab.Type == 1) {
                // 金币百分比 +- ;
                mapData.addGold(getNowIndex(), mapData.getGold(getNowIndex()) * tab.Value / 100);
            } else if (tab.Type == 2) {
                // 数字 金币 +-
                mapData.addGold(getNowIndex(), tab.Value);
                // 检查是否 死亡
                checkDie(nowPlayer);
            }
            build = builder.setTime(0).setEvent(tab.Id).setUid(nowPlayer.uid).build();
            sendMsgToAll(ECode.MoveTargetEnd, build.toByteArray());
            checkDie(nowPlayer);
            actionEnd();
            return;
        } else if (grid.type == 2) {
            // 弹出商店,商店操作时间 30s
            nowBuyList.clear();
            build = builder.setTime(Const.AwaitShop).setUid(nowPlayer.uid).build();
            nextRun = System.currentTimeMillis() + Const.AwaitShop * 1000;
            battleStatus = BattleStatus.AwaitShop;
            sendMsgToAll(ECode.MoveTargetEnd, build.toByteArray());
            return;
        } else {
            // 该建筑的类型;
            BuildType tab = DataConfigManager.BuildTypeContainer.get(grid.type);
            // 需要判断是谁的建筑;
            if (getNowIndex() != grid.index) {
                // 别人的建筑,直接扣钱
                // 过路费
                int moveGold = tab.MoveGold[grid.lv - 1];
                // 判断过路费 够不够,不够就game over;
                mapData.addGold(grid.index, moveGold);
                mapData.addGold(getNowIndex(), -moveGold);

                build = builder.setTime(0).setUid(nowPlayer.uid).build();
                sendMsgToAll(ECode.MoveTargetEnd, build.toByteArray());

                checkDie(nowPlayer);
                // 下一个
                actionEnd();
                return;
            } else {
                // 自家建筑,判断是否满级了;
                if (grid.lv < tab.BuildGold.length) {
                    // 没满级,客户端询问玩家是否升级; 先检查钱 是否够升级,如果不够 直接下一个
                    int gold = mapData.getGold(getNowIndex());
                    if (gold < tab.BuildGold[grid.lv]) {
                        // 不够,下一个
                        actionEnd();
                    } else {
                        build = builder.setTime(Const.AwaitTime).setUid(nowPlayer.uid).build();
                        battleStatus = BattleStatus.AwaitSel;
                        nextRun = System.currentTimeMillis() + Const.AwaitTime * 1000;
                        sendMsgToAll(ECode.MoveTargetEnd, build.toByteArray());
                    }
                } else {
                    // 满级了,不处理  下一个
                    actionEnd();
                }
            }
        }
    }

    /**
     * 如果 移动完成时,到达的格子有需要处理的事件,才会允许玩家 请求这个方法;个别情况不执行这个方法
     */
    public void awaitSel(Player player, int event) {
        if (!check(player, BattleStatus.AwaitSel)) {
            return;
        }
        // 查看当前格子 能触发什么事件
        Grid grid = mapData.getPlayerGrid(getNowIndex());
        // 0:空地, 1:事件点(不需要处理),2:商店;  收费类建筑:3商场,4宾馆,5饭店
        if (grid.type == 0) {
            // 玩家传过来的是 建筑类型; 或者0 不建筑
            if (event != 0) {
                grid.type = event;
                grid.index = getNowIndex();
                grid.lv = 1;
                BuildType type = DataConfigManager.BuildTypeContainer.get(event);
                if (mapData.getGold(getNowIndex()) >= type.BuildGold[0]) {
                    mapData.addGold(getNowIndex(), -type.BuildGold[0]);
                    // 通知其他人,该地块状态改变
                    Battle.FuncResponse.Builder builder = Battle.FuncResponse.newBuilder();
                    Battle.FuncResponse build = builder.setUid(nowPlayer.uid).setEvent(event).build();
                    sendMsgToAll(ECode.FuncRequest, build.toByteArray());
                }
            }
            // 下一个
            actionEnd();
            return;
        } else if (grid.type == 1) {
            // 事件 不需要处理
        } else {
            // 0不升级,1升级
            if (event != 0) {
                // 检查钱够不,其实不需要检查,在进入该格子的时候,检查是否够升级
                BuildType tab = DataConfigManager.BuildTypeContainer.get(grid.type);
                if (mapData.getGold(getNowIndex()) < tab.BuildGold[grid.lv]) {
                    // 钱不够,通知所有人 下一个
                    actionEnd();
                    return;
                }
                // 扣钱,
                mapData.addGold(getNowIndex(), -tab.BuildGold[grid.lv]);
                // 该建筑的类型;
                grid.lv += 1;
                // 通知所有人,该建筑升级1
                Battle.FuncResponse.Builder builder = Battle.FuncResponse.newBuilder();
                Battle.FuncResponse build = builder.setUid(nowPlayer.uid).setEvent(event).build();
                sendMsgToAll(ECode.FuncRequest, build.toByteArray());
            }
            actionEnd();
            return;
        }
    }

    /**
     * 在商店购买东西
     *
     * @param player
     * @param cid
     */
    public void battleShop(Player player, int cid) {
        if (!check(player, BattleStatus.AwaitShop)) {
            return;
        }
        // 查看当前格子 是否商店; 2:商店
        Grid grid = mapData.getPlayerGrid(getNowIndex());
        if (grid.type != 2) {
            return;
        }
        if (cid == 0) { // 结束购买了
            // 将购买花费,发送给所有玩家
            int count = 0;
            for (Integer i : nowBuyList) {
                ItemTab tab = DataConfigManager.ItemTabContainer.get(i);
                count += tab.Price;
            }
            mapData.addGold(getNowIndex(), -count);

            // 道具入库
            itemManager.addItem(getNowIndex(), nowBuyList);

            Battle.FuncResponse.Builder builder = Battle.FuncResponse.newBuilder();
            builder.setUid(player.uid).setEvent(count);
            sendMsgToAll(ECode.FuncRequest, builder.build().toByteArray());
            actionEnd();
            return;
        }
        // 检查配置表是否有这个道具
        ItemTab itemTab = DataConfigManager.ItemTabContainer.get(cid);
        if (itemTab == null) {
            return;
        }

        // 每种道具 在每次遇到商店时,只能买一个; 检查是否能购买
        if (nowBuyList.contains(cid)) {
            return;
        }
        // 身上钱不够
        if (mapData.getGold(getNowIndex()) < itemTab.Price) {
            return;
        }
        nowBuyList.add(cid);
        Battle.BattleShopResponse.Builder builder = Battle.BattleShopResponse.newBuilder();
        Battle.BattleShopResponse build = builder.setCid(cid).build();
        ATask.sendMsgToOne(player.uid, ECode.BattleShop, build.toByteArray());
    }


    /**
     * 某个玩家 行动结束; 初始化 某些 临时变量
     */
    private void actionEnd() {
        // 下一个玩家开始 行动,先通知 MapData,该玩家行动结束了
        mapData.MoveEnd(getNowIndex());

        Index++;
        int sign = mapTab.Num; //
        while (sign > 0) {
            boolean dieStatus = mapData.getPlayerDieStatus(Index % Players.size());
            if (!dieStatus) {
                Index++;
                sign--;
            } else {
                break;
            }
        }

        sendRoll();
    }


    /**
     * 发送消息给所有在房间的人
     */
    private void sendMsgToAll(ECode code, byte[] msg) {
        // LoggerManager.info(Arrays.toString(mapData.golds));
        for (Player player : Players) {
            if (player.battleData.rid == id) {
                ATask.sendMsgToOne(player.uid, code, msg);
            }
        }
        // 给观战人发消息
        ATask.sendMsgToList(LookPlayer, code, msg);
    }


    private int getNowIndex() {
        return Index % Players.size();
    }

    /**
     * 该玩家 对局结束了
     *
     * @param player
     */
    private void playerOver(Player player) {
        Battle.BattleOverResponse.Builder builder = Battle.BattleOverResponse.newBuilder();
        Battle.BattleOverResponse build = builder.setOver(false).setUid(player.uid).build();
        sendMsgToAll(ECode.BattleOver, build.toByteArray());

        // 检查对局是否 结束了; 当对局中只有一个玩家还 存活,就结束了
        int index = -1;// 存活的角色 下标;
        for (int i = 0; i < Players.size(); i++) {
            if (mapData.getPlayerDieStatus(i)) {
                if (index >= 0) {
                    // 超过1个人存活
                    return;
                } else {
                    index = i;
                }
            }
        }
        // 只有一个人存活了
        builder = Battle.BattleOverResponse.newBuilder();
        build = builder.setOver(true).setUid(Players.get(index).uid).build();
        sendMsgToAll(ECode.BattleOver, build.toByteArray());
        // 修改房间状态为 结束; 10秒后 关闭
        roomStatus = RoomStatus.Over;
        battleStatus = BattleStatus.Non;// 对局结束了,不再处理请求
        nextRun = System.currentTimeMillis() + 10 * 1000;
        // 做统计等数据;
    }

    private void checkDie(Player player) {
        if (!mapData.getPlayerDieStatus(getIndex(player))) {
            LoggerManager.info(nowPlayer.nickname + "钱花完了,通知死亡");
            // 通知所有人,该玩家 失败了
            playerOver(nowPlayer);
        }
    }

    private int getIndex(Player player) {
        for (int i = 0; i < Players.size(); i++) {
            if (Players.get(i).uid == player.uid) {
                return i;
            }
        }
        return -1;
    }

    public void leave(Player player) {
        for (int i = 0; i < Players.size(); i++) {
            if (player.uid == Players.get(i).uid) {
                // 角色处于死亡状态,或者对局结束了,都可以离开
                if (!mapData.getPlayerDieStatus(i) || roomStatus == RoomStatus.Over) {
                    // 确实是死亡状态,可以离开了
                    player.battleData.rid = 0;
                    player.battleData.mid = 0;
                    player.room = null;
                    Hall.LeaveBattleResponse build = Hall.LeaveBattleResponse.newBuilder().build();
                    ATask.sendMsgToOne(player.uid, ECode.LeaveBattle, build.toByteArray());
                }
            }
        }

        for (Player item : LookPlayer) {
            if (item.uid == player.uid) {
                item.battleData.rid = 0;
                item.battleData.mid = 0;
                item.room = null;
                Hall.LeaveBattleResponse build = Hall.LeaveBattleResponse.newBuilder().build();
                ATask.sendMsgToOne(item.uid, ECode.LeaveBattle, build.toByteArray());
            }
        }
    }

    /**
     * 关闭房间,不再允许人访问
     */
    private void closeRoom() {
        roomStatus = RoomStatus.Non;
        for (Player player1 : Players) {
            if (player1.battleData.rid == id) {
                player1.battleData.rid = 0;
                player1.battleData.mid = 0;
                player1.room = null;
            }
        }
        Players.clear();
        for (Player player1 : LookPlayer) {
            if (player1.battleData.rid == id) {
                player1.battleData.rid = 0;
                player1.battleData.mid = 0;
                player1.room = null;
            }
        }
        LookPlayer.clear();
    }

    /**
     * 检查是否该当前玩家 操作了,以及操作是否 符合要求
     *
     * @return
     */
    private boolean check(Player player, BattleStatus status) {
        if (player.uid != Players.get(Index % Players.size()).uid) {
            return false;
        }
        // 如果不是 等待玩家 roll的状态,不处理
        if (battleStatus != status) {
            return false;
        }
        return true;
    }

}
